//
// Created by neo on 25-8-1.
//

#include "VkGPUTexture.h"

#include "VkGPUHelper.h"
#include "log/Log.h"
VkGPUTexture::VkGPUTexture(const std::shared_ptr<VkGPUContext> &gpuCtx,
                           const float width, const float height) {
  this->width = width;
  this->height = height;
  this->gpuCtx = gpuCtx;
}

VkGPUTexture::VkGPUTexture(
    const std::shared_ptr<VkGPUContext> &gpuCtx, const float width,
    const float height, const std::shared_ptr<VkGPUBuffer> &imageStageBuffer) {
  this->width = width;
  this->height = height;
  this->gpuCtx = gpuCtx;
  this->imageStageBuffer = imageStageBuffer;
}

VkResult VkGPUTexture::CreateTexture() {
  const std::vector<uint32_t> queueFamilies = {0};
  VkResult ret = VK_SUCCESS;

  const VkPhysicalDeviceMemoryProperties memoryProperties =
      gpuCtx->GetMemoryProperties();

  ret = VkGPUHelper::CreateImageAndBindMem(
      gpuCtx->GetCurrentDevice(), this->width, this->height, VK_IMAGE_TYPE_2D,
      VK_FORMAT_R8G8B8A8_SRGB,
      VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
      VK_SHARING_MODE_EXCLUSIVE, VK_IMAGE_LAYOUT_UNDEFINED, &memoryProperties,
      queueFamilies, &this->textureImage, &this->textureImageMemory);
  if (ret != VK_SUCCESS) {
    Logger() << Logger::ERROR << "Failed to create texture image" << std::endl;
    return ret;
  }

  ret = VkGPUHelper::CreateImageView(
      this->gpuCtx->GetCurrentDevice(), this->textureImage,
      VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT,
      &this->textureImageView);
  if (ret != VK_SUCCESS) {
    Logger() << "Failed to create texture imageview" << std::endl;
    return ret;
  }

  ret = VkGPUHelper::CreateSampler(this->gpuCtx->GetCurrentDevice(),
                                   &this->textureSampler);
  if (ret != VK_SUCCESS) {
    Logger() << "Failed to create texture sampler" << std::endl;
    return ret;
  }

  if (imageStageBuffer == nullptr) {
    imageStageBuffer = std::make_shared<VkGPUBuffer>(gpuCtx);
    ret = imageStageBuffer->AllocateAndBind(GPU_BUFFER_TYPE_STORAGE_SHARED,
                                            this->width * this->height *
                                                sizeof(uint32_t));
    if (ret != VK_SUCCESS) {
      Logger() << "Failed to allocate image stage buffer" << std::endl;
    }
    imageStageBufferAllocated = true;
  }

  return ret;
}

void VkGPUTexture::Destroy() {
  if (textureImage != VK_NULL_HANDLE) {
    vkDestroyImage(this->gpuCtx->GetCurrentDevice(), textureImage, nullptr);
    textureImage = VK_NULL_HANDLE;
  }
  if (textureImageView != VK_NULL_HANDLE) {
    vkDestroyImageView(this->gpuCtx->GetCurrentDevice(), textureImageView,
                       nullptr);
    textureImageView = VK_NULL_HANDLE;
  }
  if (textureSampler != VK_NULL_HANDLE) {
    vkDestroySampler(this->gpuCtx->GetCurrentDevice(), textureSampler, nullptr);
    textureSampler = VK_NULL_HANDLE;
  }
  if (textureImageMemory != VK_NULL_HANDLE) {
    vkFreeMemory(this->gpuCtx->GetCurrentDevice(), textureImageMemory, nullptr);
    textureImageMemory = VK_NULL_HANDLE;
  }
  if (this->imageStageBufferAllocated && this->imageStageBuffer != nullptr) {
    this->imageStageBuffer->Destroy();
    this->imageStageBuffer = nullptr;
  }
}

VkGPUTexture::~VkGPUTexture() { this->Destroy(); }